package com.lzy.product.common.shiro;

import com.google.common.base.Objects;
import com.lzy.base.apis.SysResourceServiceApi;
import com.lzy.base.apis.SysUserAdminServiceApi;
import com.lzy.base.constants.Constants;
import com.lzy.base.dao.SysRoleMapperExt;
import com.lzy.base.dao.SysUserRoleMapperExt;
import com.lzy.base.dtos.SysResourceDto;
import com.lzy.base.dtos.SysUserAdminDto;
import com.lzy.base.entity.SysRole;
import com.lzy.base.entity.SysUserRole;
import com.lzy.base.exception.AccountFrozenException;
import com.lzy.base.exception.AuditFailureException;
import com.lzy.base.exception.UnderReviewException;
import com.lzy.base.utils.PasswordUtil;
import com.lzy.common.apis.ShopServiceApi;
import com.lzy.common.apis.StoreServiceApi;
import com.lzy.framework.core.utils.CollectionUtils;
import com.lzy.framework.core.utils.StringUtil;
import com.lzy.framework.tools.security.Encodes;
import com.lzy.product.common.util.security.UsernamePasswordCaptchaToken;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.jdbc.JdbcRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;

import javax.annotation.PostConstruct;
import java.io.Serializable;
import java.util.HashSet;
import java.util.List;

/**
 * 用户登录授权service(shiro Realm)
 *
 * @author lc
 * @date 2016年12月31日 下午4:47:14
 */
public class UserRealm extends JdbcRealm {

    @Autowired
    private SysUserAdminServiceApi sysUserAdminService;
    @Autowired
    private SysUserRoleMapperExt sysUserRoleMapper;
    @Autowired
    private SysResourceServiceApi sysResourceServiceApi;
    @Autowired
    private SysRoleMapperExt sysRoleMapper;
    @Autowired
    StoreServiceApi storeServiceApi;
    @Autowired
    ShopServiceApi shopServiceApi;

    /**
     * 认证回调函数,登录时调用.
     * * 商家->商铺（一对多）
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuditFailureException, AuthenticationException, UnderReviewException {

        UsernamePasswordCaptchaToken token = (UsernamePasswordCaptchaToken) authcToken;

        // 验证码判断
        if (doCaptchaValidate(token)) {

        }

        SimpleAuthenticationInfo simpleAuthenticationInfo = null;
        SysUserAdminDto sysUserAdminDto = sysUserAdminService.selectByLoginName(token.getUsername());
        if (null == sysUserAdminDto) {
            throw new UnknownAccountException();
        }

        // 状态判断
        if (Constants.USER_ACCOUNT_LOGIN_LOCKED.equals(sysUserAdminDto.getState())) {
            throw new LockedAccountException();
        } else if (Constants.USER_ACCOUNT_SYSTEM_LOCKED.equals(sysUserAdminDto.getState())) {
            throw new AccountFrozenException();
        }
        //赋予角色
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        List<SysUserRole> sysUserRoles = sysUserRoleMapper.selectForRole(sysUserAdminDto.getId());
        if (sysUserRoles != null && CollectionUtils.isNotEmpty(sysUserRoles)) {
            for (int i = 0; i < sysUserRoles.size(); i++) {
                SysRole role = sysRoleMapper.selectByPrimaryKey(sysUserRoles.get(i).getRoleId());
                if (role != null) {
                    if (role.getId()==3){
                        throw new UnknownAccountException();
                    }
                }
                if (role != null) {
                    info.addRole(role.getRlname());
                }
            }
        }
        byte[] salt = Encodes.decodeHex(sysUserAdminDto.getSalt());
        ShiroUser shiroUser = new ShiroUser(sysUserAdminDto.getId(), sysUserAdminDto.getLoginName());
        // 设置用户session
        Session session = SecurityUtils.getSubject().getSession();
        session.setAttribute(Constants.SYS_USER, sysUserAdminDto);
        //赋予角色
        StringBuffer roles = new StringBuffer();
        HashSet<Integer> roleTypeSet = new HashSet<>();
        if (sysUserRoles != null && CollectionUtils.isNotEmpty(sysUserRoles)) {
            for (int i = 0; i < sysUserRoles.size(); i++) {
                SysRole role = sysRoleMapper.selectByPrimaryKey(sysUserRoles.get(i).getRoleId());
                if (role != null) {
                    roles.append(role.getId()).append(",");
                    roleTypeSet.add(role.getType());
                }
            }
        }
        String roleIds = roles.toString();
        if (StringUtil.trimToNull(roleIds) != null) {
            roleIds = roleIds.substring(0, roleIds.length() - 1);
        }
        //用户角色ID
        SecurityUtils.getSubject().getSession().setAttribute(Constants.SYS_USER_ROLE, roleIds);
        //角色類型信息
        SecurityUtils.getSubject().getSession().setAttribute(Constants.SYS_USER_ROLE_TYPE, roleTypeSet != null ? StringUtils.join(roleTypeSet.toArray(), ",") : null);
        simpleAuthenticationInfo = new SimpleAuthenticationInfo(shiroUser, sysUserAdminDto.getPassword(), ByteSource.Util.bytes(salt), getName());
        removeUserAuthorizationInfoCache(token.getUsername());
        return simpleAuthenticationInfo;
    }


    /**
     * 授权查询回调函数, 进行鉴权但缓存中无用户的授权信息时调用.
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        ShiroUser shiroUser = (ShiroUser) principals.getPrimaryPrincipal();
        SysUserAdminDto user = sysUserAdminService.selectByLoginName(shiroUser.loginName);

        //把principals放session中 key=userId value=principals
        SecurityUtils.getSubject().getSession().setAttribute(String.valueOf(user.getId()),
                SecurityUtils.getSubject().getPrincipals());
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

        //赋予角色
        List<SysUserRole> sysUserRoles = sysUserRoleMapper.selectForRole(user.getId());
        if (sysUserRoles != null && CollectionUtils.isNotEmpty(sysUserRoles)) {
            for (int i = 0; i < sysUserRoles.size(); i++) {
                SysRole role = sysRoleMapper.selectByPrimaryKey(sysUserRoles.get(i).getRoleId());
                if (role != null) {
                    info.addRole(role.getRlname());
                }
            }
        }

        //赋予权限
        if (user != null) {
            List<SysResourceDto> sysResources = sysResourceServiceApi.selectResource(user.getId(), null, null,
                    Constants.RESOURCE_STATUS_ABLE, null); //UserUtil.getCurrentUser().getDataType());
            for (int i = 0; i < sysResources.size(); i++) {
                if (StringUtils.isNotBlank(sysResources.get(i).getCode())) {
                    info.addStringPermission(sysResources.get(i).getCode());
                }
            }
        }

        //设置登录次数、时间
        sysUserAdminService.update(user);
        return info;
    }

    public void removeUserAuthorizationInfoCache(String username) {
        SimplePrincipalCollection pc = new SimplePrincipalCollection();
        pc.add(username, super.getName());
        super.clearCachedAuthorizationInfo(pc);
    }

    /**
     * 验证码校验
     *
     * @param token
     * @return boolean
     */
    protected boolean doCaptchaValidate(UsernamePasswordCaptchaToken token) {
//        String captcha = (String) SecurityUtils.getSubject().getSession().getAttribute("KAPTCHA_SESSION_KEY");
//        if (captcha != null &&!captcha.equalsIgnoreCase(token.getCaptcha())){
//            throw new CaptchaException("验证码错误！");
//        }
        return true;
    }

    /**
     * 设定Password校验的Hash算法与迭代次数.
     */
    @SuppressWarnings("static-access")
    @PostConstruct
    public void initCredentialsMatcher() {
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher(PasswordUtil.hashAlgorithm);
        matcher.setHashIterations(Integer.valueOf(PasswordUtil.hashIntegrations));
        setCredentialsMatcher(matcher);
    }

    /**
     * 自定义Authentication对象，使得Subject除了携带用户的登录名外还可以携带更多信息.
     */
    public static class ShiroUser implements Serializable {
        private static final long serialVersionUID = 1L;
        public Long id;
        public String loginName;

        public ShiroUser(Long id, String loginName) {
            this.id = id;
            this.loginName = loginName;
            // TODO GET　companyId & orgId
        }

        public Long getId() {
            return id;
        }

        /**
         * 本函数输出将作为默认的<shiro:principal/>输出.
         */
        @Override
        public String toString() {
            return loginName;
        }

        /**
         * 重载hashCode,只计算loginName;
         */
        @Override
        public int hashCode() {
            return Objects.hashCode(loginName);
        }

        /**
         * 重载equals,只计算loginName;
         */
        @Override
        public boolean equals(Object obj) {
            if (this == obj) {
                return true;
            }
            if (obj == null) {
                return false;
            }
            if (getClass() != obj.getClass()) {
                return false;
            }
            ShiroUser other = (ShiroUser) obj;
            if (loginName == null) {
                if (other.loginName != null) {
                    return false;
                }
            } else if (!loginName.equals(other.loginName)) {
                return false;
            }
            return true;
        }
    }

    @Override
    public void clearCachedAuthorizationInfo(PrincipalCollection principals) {
        super.clearCachedAuthorizationInfo(principals);
    }

    @Override
    public void clearCachedAuthenticationInfo(PrincipalCollection principals) {
        super.clearCachedAuthenticationInfo(principals);
    }

    @Override
    public void clearCache(PrincipalCollection principals) {
        super.clearCache(principals);
        clearAllCache();
    }

    public void clearAllCachedAuthorizationInfo() {
        if (getAuthorizationCache() != null) {
            getAuthorizationCache().clear();
        }
    }

    public void clearAllCachedAuthenticationInfo() {
        if (getAuthenticationCache() != null) {
            getAuthenticationCache().clear();
        }
    }

    public void clearAllCache() {
        clearAllCachedAuthenticationInfo();
        clearAllCachedAuthorizationInfo();
    }

}
